﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using TomanuExtensions;
using RaytracerLib.Rays;

namespace RaytracerLib.MathLib
{
    public static class FresnellMath
    {
        public static double FresnelReflectionFactor(Vector3 a_reflected_or_inverse_in,
            Vector3 a_normal, double a_n1, double a_n2)
        {
            Debug.Assert(a_reflected_or_inverse_in.IsNormalized);
            Debug.Assert(a_normal.IsNormalized);
            Debug.Assert(a_n1 >= 1);
            Debug.Assert(a_n2 >= 1);

            double ca = a_normal * a_reflected_or_inverse_in;

            Debug.Assert(ca.IsAlmostRelativeGreaterOrEqualThen(0));
            Debug.Assert(ca.IsAlmostRelativeLessOrEqualThen(1));

            ca = DoubleExtensions.Limit(ca, 0, 1);

            double sa = Math.Sqrt(1 - ca * ca);
            double sb = sa * a_n1 / a_n2;

            if (sb > 1)
                return 1;

            double cb = Math.Sqrt(1 - sb * sb);
            double can1 = ca * a_n1;
            double can2 = ca * a_n2;
            double cbn1 = cb * a_n1;
            double cbn2 = cb * a_n2;
            double rs = Math.Pow((can1 - cbn2) / (can1 + cbn2), 2);
            double rt = Math.Pow((cbn1 - can2) / (cbn1 + can2), 2);
            double result = (rs + rt) / 2;

            Debug.Assert(result >= 0);
            Debug.Assert(result <= 1);

            return result;
        }

        public static double FresnelReflectionFactor(Ray a_reflected_ray, Ray a_refracted_ray,
            Vector3 a_normal, double a_n1, double a_n2)
        {
            Debug.Assert(a_normal.IsNormalized);
            Debug.Assert(a_n1 >= 1);
            Debug.Assert(a_n2 >= 1);

            if (a_refracted_ray == null)
                return 1;

            double ca = a_normal * a_reflected_ray.Dir;
            double cb = -(a_normal * a_refracted_ray.Dir);

            Debug.Assert(ca.IsAlmostRelativeGreaterOrEqualThen(0));
            Debug.Assert(cb.IsAlmostRelativeGreaterOrEqualThen(0));
            Debug.Assert(ca.IsAlmostRelativeLessOrEqualThen(1));
            Debug.Assert(cb.IsAlmostRelativeLessOrEqualThen(1));

            ca = DoubleExtensions.Limit(ca, 0, 1);
            cb = DoubleExtensions.Limit(ca, 0, 1);

            double can1 = ca * a_n1;
            double can2 = ca * a_n2;
            double cbn1 = cb * a_n1;
            double cbn2 = cb * a_n2;
            double rs = Math.Pow((can1 - cbn2) / (can1 + cbn2), 2);
            double rt = Math.Pow((cbn1 - can2) / (cbn1 + can2), 2);

            double result = (rs + rt) / 2;

            Debug.Assert(result >= 0);
            Debug.Assert(result <= 1);

            return result;
        }
    }
}
